/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package uoc.libwatermarking.sync.FrameOriented;

import java.util.List;

/**
 *
 * @author rbuj
 */
public class SintSextByte {

    /**
     * The sync message.
     */
    private int sync_message;
    /**
     * The number of bits of the sync message.
     */
    private int num_bits_sync_message;
    /**
     * The number of samples for codifying each bit of the sync message.
     */
    private int num_samples_per_bit;

    /**
     * Primary constructor method.
     *
     * @param _sync_message The sync message
     * @param _num_bits_sync_message The number of bits of the sync message
     * @param _num_samples_per_bit The number of samples for codifying one bit
     * of the sync message.
     *
     * @since 2013-08-19
     */
    public SintSextByte(int _sync_message, int _num_bits_sync_message,
            int _num_samples_per_bit) {
        sync_message = _sync_message;
        num_bits_sync_message = _num_bits_sync_message;
        num_samples_per_bit = _num_samples_per_bit;
    }

    /**
     * Put the sync mark in a list of samples, that list is passed as
     * argument by reference.
     *
     * @param lframe Non-empty list of audio samples.
     * @since 2013-08-19
     */
    public void putSynMark(List<Byte> lframe) {
        boolean bit = false;
        int toValidate = lframe.size() - size();
        if (lframe.size() < size()) {
            throw new UnsupportedOperationException("Not enough samples for "
                    + "putting the sync mark");
        }
        int code = sync_message;
        for (int i = 0; i < num_bits_sync_message; i++) {
            bit = (code & 0x00000001) == 1;
            code = code >> 1;
            insertBit(toValidate, bit, lframe);
            toValidate += num_samples_per_bit;
        }
    }

    /**
     * Get the number of samples are being used to put the sync mark.
     *
     * @return Number of samples.
     * @since 2013-08-19
     */
    public short size() {
        return (short) (num_bits_sync_message * num_samples_per_bit);
    }

    /**
     * As of the given position in a list, embed one bit of the sync mark.
     *
     * @param start The position to begin the codification of the sync bit.
     * @param bit An information bit that belongs to the sync mark.
     * @param lframe Non-empty list of audio samples.
     *
     * @since 2013-08-19
     */
    private void insertBit(int start, boolean bit, List<Byte> lframe) {
        byte aux;
        short diff;
        // compute sInt
        double sInt = 0;
        for (int i = 0; i < num_samples_per_bit - 2; i++) {
            sInt += lframe.get(i + start + 1);
        }
        sInt = sInt / (num_samples_per_bit - 2);
        // compute sExt
        double sExt = lframe.get(start)
                + lframe.get(start + num_samples_per_bit - 1);
        sExt = sExt / 2;
        // diff
        diff = (short) (((sInt * (num_samples_per_bit - 2) - (sExt * 2))
                / (num_samples_per_bit - 2)));
        // embed a 1 (sInt > sExt)
        if (bit) {
            if (diff <= 0) {
                for (int i = 0; i < num_samples_per_bit - 2; i++) {
                    aux = (byte) (lframe.get(i + start + 1) - diff + 1);
                    lframe.set(i + start + 1, aux);
                }
            }
        } // embed a 0 (sInt < sExt)
        else {
            if (diff >= 0) {
                for (int i = 0; i < num_samples_per_bit - 2; i++) {
                    aux = (byte) (lframe.get(i + start + 1) - diff - 1);
                    lframe.set(i + start + 1, aux);
                }
            }
        }
    }

    /**
     * Checks if a list of audio samples has the sync mark.
     *
     * @param lframe Non-empty list of audio samples.
     * @return True if the watermark was found, otherwise false.
     * @since 2013-08-20
     */
    public boolean testSynMark(List<Byte> lframe) {
        boolean bit;
        boolean expected;
        int code = sync_message;
        int toValidate = 0;

        for (int i = 0; i < num_bits_sync_message; i++) {
            expected = (code & 0x00000001) == 1;
            code = code >> 1;
            bit = getBit(toValidate, lframe);
            if (bit != expected) {
                return false;
            }
            toValidate += num_samples_per_bit;
        }
        return true;
    }

    /**
     * Extract the bit of the mark from a list of samples.
     *
     * @param start The position to begin the decodification of the sync bit.
     * @param lframe Non-empty list of audio samples.
     * @return The embedded value of a portion of the sync mark.
     * @since 2013-08-20
     */
    private boolean getBit(int start, List<Byte> lframe) {
        double diff;
        //Calculating tInt
        double sInt = 0;
        for (int i = 0; i < num_samples_per_bit - 2; i++) {
            sInt += lframe.get(i + start + 1);
        }
        sInt = sInt / (num_samples_per_bit - 2);
        //Calculating tExt
        double sExt = lframe.get(start)
                + lframe.get(start + num_samples_per_bit - 1);
        sExt = sExt / 2;
        // return the codified bit
        diff = sInt - sExt;
        if (diff > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Get all the sync mark as a text string.
     *
     * @return A text string.
     * @since 2013-08-21
     */
    @Override
    public String toString() {
        int bit;
        String str = "";
        int code = sync_message;
        for (int i = 0; i < num_bits_sync_message; i++) {
            bit = (code & 0x00000001);
            code = code >> 1;
            str += bit;
        }
        return str;
    }
}
